const cap = require('cap');
const readline = require('readline');
const winston = require("winston");
const net = require('net');
const zlib = require('zlib');

const { VERSION } = require('./utils/constants')
const configManager = require('./utils/ConfigManager');
const logger = require('./utils/logger')
const { initGlobalErrorHandler } = require('./utils/errorHandler')


initGlobalErrorHandler(logger);


// 设置控制台输出编码为UTF-8（修复乱码问题）
if (process.platform === 'win32') {
    process.stdout.setDefaultEncoding('utf8');
    process.stderr.setDefaultEncoding('utf8');
}
const express = require('express');
const pb = require('./algo/pb');
// 优先使用源码的packet处理器，提供更准确的治疗/承伤分离
let SRPacketProcessor = null;
try {
    // 优先使用源码版本
    SRPacketProcessor = require('./StarResonanceDamageCounter/algo/packet');
    logger.info('✅ 已加载源码PacketProcessor - 支持精确的治疗/承伤分离');
} catch (e1) {
    try {
        // 回退到项目内版本
        SRPacketProcessor = require('./algo/packet');
        logger.info('✅ 使用项目内PacketProcessor');
    } catch (e2) {
        logger.info('❌ 未找到PacketProcessor - 使用内置解析器');
        SRPacketProcessor = null;
    }
}
const Readable = require("stream").Readable;
const fs = require('fs');
const path = require('path');
const Cap = cap.Cap;
const decoders = cap.decoders;
const PROTOCOL = decoders.PROTOCOL;
const print = logger.info;
const app = express();

const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});
let devices = [];
try {
    devices = cap.deviceList();
    if (!devices || devices.length === 0) {
        logger.error('无法获取网卡列表，可能是权限不足或驱动问题');
        process.exit(1);
    }
    logger.info(`✅成功获取 ${devices.length} 个网卡`);
} catch (error) {
    logger.error(' 网卡列表获取失败:', error.message);
    logger.error('请检查：');
    logger.error('1. 是否以管理员权限运行');
    logger.error('2. 是否安装了 WinPcap 或 Npcap');
    logger.error('3. 网卡驱动是否正常');
    process.exit(1);
}

function ask(question) {
    return new Promise(resolve => {
        rl.question(question, answer => {
            resolve(answer);
        });
    });
}

class Lock {
    constructor() {
        this.queue = [];
        this.locked = false;
    }

    async acquire() {
        if (this.locked) {
            return new Promise((resolve) => this.queue.push(resolve));
        }
        this.locked = true;
    }

    release() {
        if (this.queue.length > 0) {
            const nextResolve = this.queue.shift();
            nextResolve();
        } else {
            this.locked = false;
        }
    }
}

// 自动检测合适的网卡（优化版：基于地址匹配默认路由）
async function autoDetectNetworkDevice() {
    try {
        logger.info('🔍 正在自动检测合适的网卡...');

        // 方法1: 尝试基于默认路由精确匹配
        const defaultDevice = await findDefaultNetworkDeviceByRoute();
        if (defaultDevice) {
            logger.info(`✅ 通过默认路由匹配到网卡: ${defaultDevice.index}. ${defaultDevice.description}`);
            return defaultDevice;
        }

        // 方法2: 基于地址筛选（优先选择有真实 IPv4 的网卡）
        const deviceWithValidIp = findDeviceWithValidIP();
        if (deviceWithValidIp) {
            logger.info(`✅ 通过 IPv4 地址匹配到网卡: ${deviceWithValidIp.index}. ${deviceWithValidIp.description}`);
            return deviceWithValidIp;
        }

        // 方法3: 回退到关键词匹配（保留原逻辑作为兜底）
        const keywordDevice = findDeviceByKeywords();
        if (keywordDevice) {
            logger.info(`⚠️ 通过关键词匹配到网卡: ${keywordDevice.index}. ${keywordDevice.description}`);
            return keywordDevice;
        }

        // 最后兜底：选择第一个可用网卡
        if (devices.length > 0) {
            logger.info(`⚠️ 使用默认网卡: 0. ${devices[0].description}`);
            return { index: 0, name: devices[0].name, description: devices[0].description };
        }

        logger.info('❌ 未找到可用的网卡');
        return null;

    } catch (error) {
        logger.info(`❌ 网卡检测失败: ${error.message}`);
        return null;
    }
}

// 通过默认路由精确匹配网卡
async function findDefaultNetworkDeviceByRoute() {
    try {
        const { exec } = require('child_process');
        const stdout = await new Promise((resolve, reject) => {
            exec('route logger.info 0.0.0.0', { timeout: 3000 }, (error, stdout) => {
                if (error) reject(error);
                else resolve(stdout);
            });
        });

        // 提取默认网关条目的本地 IPv4（Windows route logger.info 第 4 列通常为接口地址）
        const defaultLocalIp = stdout
            .split(/\r?\n/)
            .map(l => l.trim())
            .filter(l => /^0\.0\.0\.0\s/.test(l))
            .map(l => l.split(/\s+/)[3])
            .find(Boolean);

        if (!defaultLocalIp) return null;

        // 用地址精确匹配设备
        for (let i = 0; i < devices.length; i++) {
            const device = devices[i];
            const addresses = Array.isArray(device.addresses) ? device.addresses : [];

            for (const addr of addresses) {
                if (addr && addr.addr === defaultLocalIp) {
                    return { index: i, name: device.name, description: device.description };
                }
            }
        }
        return null;
    } catch (error) {
        return null;
    }
}

// 查找有有效 IPv4 地址的网卡
function findDeviceWithValidIP() {
    const isValidIPv4 = (ip) => {
        if (!ip) return false;
        // 排除本地回环、APIPA、多播等
        return !ip.startsWith('127.') &&
            !ip.startsWith('169.254.') &&
            !ip.startsWith('224.') &&
            !ip.startsWith('255.') &&
            ip !== '0.0.0.0';
    };

    for (let i = 0; i < devices.length; i++) {
        const device = devices[i];
        const addresses = Array.isArray(device.addresses) ? device.addresses : [];

        if (addresses.some(addr => isValidIPv4(addr && addr.addr))) {
            return { index: i, name: device.name, description: device.description };
        }
    }
    return null;
}

// 基于关键词匹配网卡（原逻辑保留作为兜底）
function findDeviceByKeywords() {
    const preferredKeywords = ['以太网', 'Ethernet', 'Wi-Fi', 'WLAN', '无线', '有线'];
    const excludeKeywords = ['虚拟', 'Virtual', '回环', 'Loopback', 'VMware', 'Hyper-V', 'Docker', 'VPN', 'Tunnel'];

    // 先找包含优先关键词的
    for (let i = 0; i < devices.length; i++) {
        const device = devices[i];
        const description = device.description.toLowerCase();
        const name = device.name.toLowerCase();

        // 排除虚拟网卡
        const shouldExclude = excludeKeywords.some(keyword =>
            description.includes(keyword.toLowerCase()) || name.includes(keyword.toLowerCase())
        );
        if (shouldExclude) continue;

        // 检查优先关键词
        const hasPreferredKeyword = preferredKeywords.some(keyword =>
            description.includes(keyword.toLowerCase()) || name.includes(keyword.toLowerCase())
        );
        if (hasPreferredKeyword) {
            return { index: i, name: device.name, description: device.description };
        }
    }

    // 再找第一个非虚拟网卡
    for (let i = 0; i < devices.length; i++) {
        const device = devices[i];
        const description = device.description.toLowerCase();
        const name = device.name.toLowerCase();

        const shouldExclude = excludeKeywords.some(keyword =>
            description.includes(keyword.toLowerCase()) || name.includes(keyword.toLowerCase())
        );
        if (!shouldExclude) {
            return { index: i, name: device.name, description: device.description };
        }
    }

    return null;
}

// ========= 采用与新版源码一致的数据模型 =========
class StatisticData {
    constructor() {
        this.stats = { normal: 0, critical: 0, lucky: 0, crit_lucky: 0, hpLessen: 0, total: 0 };
        this.count = { normal: 0, critical: 0, lucky: 0, total: 0 };
        this.realtimeWindow = [];
        this.timeRange = [];
        this.realtimeStats = { value: 0, max: 0 };
    }
    addRecord(value, isCrit, isLucky, hpLessenValue = 0) {
        const now = Date.now();
        if (isCrit) {
            if (isLucky) this.stats.crit_lucky += value; else this.stats.critical += value;
        } else if (isLucky) {
            this.stats.lucky += value;
        } else {
            this.stats.normal += value;
        }
        this.stats.total += value;
        this.stats.hpLessen += hpLessenValue;
        if (isCrit) this.count.critical++;
        if (isLucky) this.count.lucky++;
        if (!isCrit && !isLucky) this.count.normal++;
        this.count.total++;
        this.realtimeWindow.push({ time: now, value });
        if (this.timeRange[0]) this.timeRange[1] = now; else this.timeRange[0] = now;
    }
    updateRealtimeStats() {
        const now = Date.now();
        while (this.realtimeWindow.length > 0 && now - this.realtimeWindow[0].time > 1000) this.realtimeWindow.shift();
        this.realtimeStats.value = this.realtimeWindow.reduce((s, e) => s + e.value, 0);
        if (this.realtimeStats.value > this.realtimeStats.max) this.realtimeStats.max = this.realtimeStats.value;
    }
    getTotalPerSecond() {
        if (!this.timeRange[0] || !this.timeRange[1]) return 0;
        const timeDiff = this.timeRange[1] - this.timeRange[0];
        if (timeDiff <= 0) return 0; // 避免除零
        const v = (this.stats.total / timeDiff * 1000) || 0;
        return Number.isFinite(v) ? Math.round(v) : 0; // 四舍五入到整数
    }

    // 新增：获取瞬时伤害/治疗值（基于最近1秒内的数据）
    getInstantValue() {
        const now = Date.now();
        const recentData = this.realtimeWindow.filter(e => now - e.time <= 1000);
        const total = recentData.reduce((s, e) => s + e.value, 0);
        return Math.round(total); // 四舍五入到整数
    }
    reset() {
        this.stats = { normal: 0, critical: 0, lucky: 0, crit_lucky: 0, hpLessen: 0, total: 0 };
        this.count = { normal: 0, critical: 0, lucky: 0, total: 0 };
        this.realtimeWindow = [];
        this.timeRange = [];
        this.realtimeStats = { value: 0, max: 0 };
    }
}

class UserData {
    constructor(uid) {
        this.uid = uid;
        this.name = '';
        this.damageStats = new StatisticData();
        this.healingStats = new StatisticData();
        this.takenDamage = 0;
        this.profession = '未知';
        this.skillUsage_damage = new Map();
        this.skillUsage_heal = new Map();
        this.skillUsage_takendamage = new Map();
        this.fightPoint = 0;
        // 职业稳定性跟踪（优化性能）
        this.professionStableTime = 0; // 职业稳定的时间戳
        this.professionLocked = false; // 职业是否已锁定（5秒稳定后锁定）
    }
    addDamage(attacker_uid, target_uid, skillId, damageElement, damage, hpLessenValue, isCrit, isLucky, isCauseLucky) {
        this.damageStats.addRecord(damage, isCrit, isLucky, hpLessenValue);
        // 设定一个初始值 skillId_0 为未知
        skillId = (skillId === null || skillId === undefined) ? 0 : skillId;

        if (!this.skillUsage_damage.has(skillId)) {
            logger.info(`create damage ${skillId}`)
            this.skillUsage_damage.set(skillId, {
                stats: new StatisticData(),
                skillInfo: getSkillDisplayInfo(skillId, damage, isCrit, isLucky, hpLessenValue, false, false),
                meta: { hasDamage: true, hasHealing: false, hasTaken: false }
            });
        }
        logger.info(`add damage ${skillId}`)

        const entry = this.skillUsage_damage.get(skillId);
        entry.stats.addRecord(damage, isCrit, isLucky, hpLessenValue);
        entry.stats.realtimeWindow.length = 0;
    }

    addHealing(attacker_uid, target_uid, skillId, damageElement, damage, hpLessenValue, isCrit, isLucky, isCauseLucky) {
        // 记录总治疗
        this.healingStats.addRecord(damage, isCrit, isLucky);

        skillId = (skillId === null || skillId === undefined) ? 0 : skillId;

        if (!this.skillUsage_heal.has(skillId)) {
            this.skillUsage_heal.set(skillId, {
                stats: new StatisticData(),
                skillInfo: getSkillDisplayInfo(skillId, damage, isCrit, isLucky, hpLessenValue, true, false),
                meta: { hasDamage: false, hasHealing: true, hasTaken: false }
            });
        }


        const entry = this.skillUsage_heal.get(skillId);
        entry.stats.addRecord(damage, isCrit, isLucky, hpLessenValue);
        entry.stats.realtimeWindow.length = 0;
    }

    addTakenDamage(attacker_uid, target_uid, skillId, damageElement, damage, hpLessenValue, isCrit, isLucky, isCauseLucky) {
        this.takenDamage += damage;
        // 如果提供了技能ID，记录承伤技能详情 如果没有统一记录为未知
        skillId = (skillId === null || skillId === undefined) ? 0 : skillId;

        if (!this.skillUsage_takendamage.has(skillId)) {
            this.skillUsage_takendamage.set(skillId, {
                stats: new StatisticData(),
                skillInfo: getSkillDisplayInfo(skillId, damage, isCrit, isLucky, hpLessenValue, false, true),
                meta: { hasDamage: false, hasHealing: false, hasTaken: true }
            });
        }

        const entry = this.skillUsage_takendamage.get(skillId);
        entry.stats.addRecord(damage, false, false, damage);
        entry.stats.realtimeWindow.length = 0;
    }

    // 兼容源码PacketProcessor的扩展接口
    setAttrKV(uid, key, value) {
        // 设置属性键值对，用于HP、职业等信息
        if (!this.attrs) this.attrs = {};
        this.attrs[key] = value;
    }

    updateRealtimeDps() { this.damageStats.updateRealtimeStats(); this.healingStats.updateRealtimeStats(); }
    getTotalDps() {
        // 综合DPS按全局战斗时长计算：仅在存在有效全局战斗时间且该玩家有伤害时采用
        try {
            if (typeof combatStartTime === 'number' && typeof lastCombatTime === 'number') {
                const durationMs = lastCombatTime - combatStartTime;
                if (durationMs > 0 && this.damageStats && this.damageStats.stats && this.damageStats.stats.total > 0) {
                    const dps = this.damageStats.stats.total / (durationMs / 1000);
                    return Number.isFinite(dps) ? Math.round(dps) : 0;
                }
            }
        } catch (_) { /* ignore and fallback */ }
        // 回退：使用个人事件窗口口径
        return this.damageStats.getTotalPerSecond();
    }
    getTotalHps() { return this.healingStats.getTotalPerSecond(); }
    // 新增：获取瞬时伤害和治疗值
    getInstantDps() { return this.damageStats.getInstantValue(); }
    getInstantHps() { return this.healingStats.getInstantValue(); }
    getTotalCount() {
        return {
            normal: this.damageStats.count.normal + this.healingStats.count.normal,
            critical: this.damageStats.count.critical + this.healingStats.count.critical,
            lucky: this.damageStats.count.lucky + this.healingStats.count.lucky,
            total: this.damageStats.count.total + this.healingStats.count.total,
        };
    }
    getSummary() {

        const skillUsageObj = {};
        this.skillUsage_damage.forEach((skillData, skillId) => {
            skillUsageObj[skillId] = {
                stats: skillData.stats,
                skillInfo: skillData.skillInfo
            };
        });

        return {
            realtime_dps: this.damageStats.realtimeStats.value,
            realtime_dps_max: this.damageStats.realtimeStats.max,
            total_dps: this.getTotalDps(),
            total_damage: { ...this.damageStats.stats },
            total_count: this.getTotalCount(),
            realtime_hps: this.healingStats.realtimeStats.value,
            realtime_hps_max: this.healingStats.realtimeStats.max,
            total_hps: this.getTotalHps(),
            total_healing: { ...this.healingStats.stats },
            taken_damage: this.takenDamage,
            profession: this.profession,
            name: this.name,
            fightPoint: this.fightPoint,
            skillUsage: skillUsageObj, // 添加技能使用数据
        };
    }
    setName(n) { this.name = n; }
    setProfession(p) {
        if (this.profession !== p) {
            this.profession = p;
            // 职业改变时重置稳定性跟踪
            this.professionStableTime = Date.now();
            this.professionLocked = false;
        } else if (this.profession === p && this.profession !== '未知') {
            // 职业保持相同且不是未知，检查是否达到稳定时间
            const now = Date.now();
            if (!this.professionLocked && (now - this.professionStableTime) >= 5000) {
                this.professionLocked = true;
                logger.info(`✅ 职业已锁定: UID ${this.uid} - ${this.profession} (稳定5秒)`);
            }
        }
    }
    setFightPoint(fp) { this.fightPoint = fp; }
    reset() {
        this.damageStats.reset();
        this.healingStats.reset();
        this.takenDamage = 0;
        this.profession = '未知';
        this.skillUsage_damage.clear();
        this.skillUsage_heal.clear();
        this.skillUsage_takendamage.clear();
        this.fightPoint = 0;
        // 重置职业锁定状态
        this.professionStableTime = 0;
        this.professionLocked = false;
    }
}

class UserInfoData {
    constructor(uid) {
        this.uid = uid;
        this.name = '';
        this.profession = '未知';
        this.fightPoint = 0;
    }
    setName(n) { this.name = n; }
    getName() { return this.name; }
    setProfession(p) { this.profession = p; }
    getProfession() { return this.profession; }
    setFightPoint(fp) { this.fightPoint = fp; }
    getFightPoint() { return this.fightPoint; }
}

class EnemyInfo {
    constructor(uid) {
        this.uid = uid;
        this.name = '';
    }
}

// 基于技能中文名称的精准职业判断系统
const professionSkillMap = {
    // 神射手-鹰弓
    '鹰弓': {
        keywords: ['二连矢', '光意·四连矢'],
        skillIds: ['220106', '220203', '220111']
    },
    // '鹰弓': {
    //     keywords: ['光能轰炸', '爆炸箭', '聚能射击'],
    //     skillIds: ['2288', '2289', '2233', '55231']
    // },
    // 神射手-狼弓
    '狼弓': {
        keywords: ['暴风箭矢', '狼普攻'],
        skillIds: ['220104', '1700827']
    },
    // '狼弓': {
    //     keywords: ['怒涛射击', '狂野呼唤'], 
    //     skillIds: ['2240', '220102', '1700826']
    // },
    // 巨刃-岩盾
    '岩盾': {
        keywords: ['碎星冲', '砂石斗篷', '勇者壁垒'],
        skillIds: ['1924', '1927', '50049']
    },
    // 巨刃-格挡
    '格挡': {
        keywords: ['砂石斗篷', '勇者壁垒', '沙岩守护'],
        skillIds: ['1927', '50049', '50037']
    },
    // 剑盾-光盾
    '光盾': {
        keywords: ['圣剑', '光明决心', '冷酷征伐'],
        skillIds: ['2421', '2451', '2452', '55412']
    },
    // 剑盾-防回
    '防回': {
        keywords: ['投掷盾牌', '清算', '圣光守卫'],
        skillIds: ['240101', '2412', '2413']
    },
    // 冰魔导师-冰矛
    '冰矛': {
        keywords: ['冰箭'],
        skillIds: ['121302']
    },
    // '冰矛': {
    //     keywords: ['陨星风暴', '清淹绕珠', '寒冰风暴'],
    //     skillIds: ['1262', '150107', '1216', '121501', '1257']
    // },
    // 冰魔导师-射线
    '射线': {
        keywords: ['寒冰射线'],
        skillIds: ['1241']
    },
    // '射线': {
    //     keywords: ['浪潮汇聚', '冻结寒风'],
    //     skillIds: ['150106', '1240']
    // },
    // 太刀-居合
    '居合': {
        keywords: ['居合', '一闪', '飞雷神', '无穷雷霆之力'],
        skillIds: ['1714', '1717', '1718']
    },
    // 太刀-月刃
    '月刃': {
        keywords: ['镰车', '霹雳连斩', '千雷闪影之意'],
        skillIds: ['1719', '1724', '1732', '44701']
    },
    // 长枪-空枪
    '空枪': {
        keywords: ['刹那', '飞鸟投', '锐利冲击'],
        skillIds: ['1424', '1425', '149905', '1431', '149907']
    },
    // 长枪-重装
    '重装': {
        keywords: ['螺旋击刺', '破追', '勇气风环'],
        skillIds: ['1421', '1422', '1427', '31901']
    },
    // 灵魂乐手-协奏
    '协奏': {
        keywords: ['愈合乐章', '激涌五重奏', '热情挥洒'],
        skillIds: ['55342', '2312', '55304', '2313', '2332', '55314']
    },
    // 灵魂乐手-狂音
    '狂音': {
        keywords: ['休止的治愈', '聚合乐章'],
        skillIds: ['55355', '230101', '230102', '230103', '230104']
    },
    // '狂音': {
    //     keywords: ['聚合乐章', '烈焰狂想', '英勇乐章', '万众瞩目'],
    //     skillIds: ['230101', '230102', '230103', '230104', '231001', '55301', '55341']
    // },
    // 森语者-愈合
    '愈合': {
        keywords: ['滋养', '森之祈愿', '盛放注能'],
        skillIds: ['150104', '1550', '21404', '21406']
    },
    // 森语者-惩戒
    '惩戒': {
        keywords: ['灌注', '再生脉冲', '自然庇护'],
        skillIds: ['150106', '150107', '150110', '1561']
    }
};

// 基于技能使用情况的职业判断权重系统
function determineProfessionBySkills(skillUsage) {
    const professionScores = {};

    // 初始化所有职业分数
    Object.keys(professionSkillMap).forEach(prof => {
        professionScores[prof] = 0;
    });

    // 遍历玩家使用的技能 - 支持 Map 和普通对象
    if (skillUsage) {
        const skillEntries = skillUsage instanceof Map ?
            Array.from(skillUsage.entries()) :
            Object.entries(skillUsage);

        for (const [skillId, skillData] of skillEntries) {
            const skillInfo = skillData.skillInfo || {};
            const skillName = skillInfo.name || '';
            const skillCount = skillData.stats?.count?.total || 0;

            // 为每个职业计算匹配分数
            Object.entries(professionSkillMap).forEach(([profession, config]) => {
                // 检查技能ID匹配
                if (config.skillIds.includes(skillId.toString())) {
                    professionScores[profession] += skillCount * 2; // ID匹配给予更高权重
                }

                // 检查技能名称关键词匹配
                // config.keywords.forEach(keyword => {
                //     if (skillName.includes(keyword)) {
                //         professionScores[profession] += skillCount;
                //     }
                // });
            });
        }
    }

    // 找出得分最高的职业
    let bestProfession = '未知';
    let maxScore = 0;

    Object.entries(professionScores).forEach(([profession, score]) => {
        if (score > maxScore) {
            maxScore = score;
            bestProfession = profession;
        }
    });

    // 移除频繁的职业分数日志以提高性能
    // logger.info(`职业判断分数:`, professionScores, `最佳职业: ${bestProfession} (${maxScore}分)`);

    // 如果得分太低，返回未知
    return maxScore >= 3 ? bestProfession : '未知'; // 降低阈值，让判断更敏感
}



class UserDataManager {
    constructor() {
        this.users = new Map();
        this.users_info = new Map(); // 临时
        this.activePlayers = new Map(); // 活跃
        this.lastCleanupTime = Date.now(); // 上次
        this.CLEANUP_INTERVAL = 60000;
    }
    getUser(uid) {
        if (!this.users.has(uid)) {
            this.users.set(uid, new UserData(uid));
            if (this.users_info.has(uid)) {
                this.users.get(uid).setName(this.users_info.get(uid).getName());
                this.users.get(uid).setProfession(this.users_info.get(uid).getProfession());
                this.users.get(uid).setFightPoint(this.users_info.get(uid).getFightPoint());
            }
        }
        return this.users.get(uid);
    }
    // 接口转换
    addDamage(uid, skillId, damage, isCrit, isLucky, hpLessenValue = 0) {
        this.inside_addDamage(uid, undefined, skillId, undefined, damage, hpLessenValue, isCrit, isLucky, undefined);
    }

    addHealing(uid, skillId, damageElement, damage, isCrit, isLucky, isCauseLucky, targetUid) {
        this.inside_addHealing(uid, targetUid, skillId, damageElement, damage, undefined, isCrit, isLucky, isCauseLucky);
    }

    addTakenDamage(uid, damage, skillId = null) {
        this.inside_addTakenDamage(undefined, uid, skillId, undefined, damage, undefined, undefined, undefined, undefined);
    }

    // 统一接口 后续修正方便
    inside_addDamage(attacker_uid, target_uid = null, skillId, damageElement = null, damage, hpLessenValue, isCrit, isLucky, isCauseLucky = null) {
        const u = this.getUser(attacker_uid);
        if (true & target_uid !== 75) return;
        u.addDamage(attacker_uid, target_uid, skillId, damageElement, damage, hpLessenValue, isCrit, isLucky, isCauseLucky);

        this.markPlayerActive(attacker_uid);
        this.updateProfessionSmart(u);
        updateCombatTime(); // 更新战斗时间
    }

    inside_addHealing(attacker_uid, target_uid, skillId, damageElement, damage, hpLessenValue = null, isCrit, isLucky, isCauseLucky) {
        const u = this.getUser(attacker_uid);
        u.addHealing(attacker_uid, target_uid, skillId, damageElement, damage, hpLessenValue, isCrit, isLucky, isCauseLucky);

        this.markPlayerActive(attacker_uid);
        // 恢复治疗事件触发职业智能判断（与伤害一致）
        this.updateProfessionSmart(u);
        // 仅造成治疗时不统计战斗时间（不在治疗事件中更新战斗计时）
    }

    inside_addTakenDamage(attacker_uid = null, target_uid, skillId, damageElement = null, damage, hpLessenValue = null, isCrit = null, isLucky = null, isCauseLucky = null) {
        const u = this.getUser(target_uid);
        u.addTakenDamage(attacker_uid, target_uid, skillId, damageElement, damage, hpLessenValue, isCrit, isLucky, isCauseLucky);

        this.markPlayerActive(target_uid);
        updateCombatTime(); // 更新战斗时间
    }

    // 兼容源码设置属性的接口
    setAttrKV(uid, key, value) {
        const user = this.getUser(uid);
        if (!user.attrs) user.attrs = {};
        user.attrs[key] = value;
    }

    setProfession(uid, p) {
        this.checkAndCleanup();
        if (!this.users_info.has(uid)) {
            this.users_info.set(uid, new UserInfoData(uid));
        }
        this.users_info.get(uid).setProfession(p)
        if (this.users.has(uid)) {
            this.users.get(uid).setProfession(p);
        }
    }
    setName(uid, n) {
        this.checkAndCleanup();
        if (!this.users_info.has(uid)) {
            this.users_info.set(uid, new UserInfoData(uid));
        }
        this.users_info.get(uid).setName(n)
        if (this.users.has(uid)) {
            this.users.get(uid).setName(n);
        }
    }
    setFightPoint(uid, fp) {
        this.checkAndCleanup();
        if (!this.users_info.has(uid)) {
            this.users_info.set(uid, new UserInfoData(uid));
        }
        this.users_info.get(uid).setFightPoint(fp)
        if (this.users.has(uid)) {
            this.users.get(uid).setFightPoint(fp);
        }
    }
    updateAllRealtimeDps() { for (const u of this.users.values()) u.updateRealtimeDps(); }

    // 智能职业判断更新（优化性能：5秒稳定后停止检测）
    updateProfessionSmart(user) {
        // 如果职业已锁定（5秒稳定），跳过检测以节省性能
        if (user.professionLocked) {
            return;
        }

        // 如果当前职业不是"未知"，检查稳定性（避免重复expensive计算）
        if (user.profession !== '未知') {
            user.setProfession(user.profession); // 触发稳定性检查
            return;
        }

        // 只有在职业为"未知"时才进行expensive的职业检测
        const smartProfession = determineProfessionBySkills(user.skillUsage_damage);

        if (smartProfession !== '未知') {
            logger.info(`🎯 智能职业判断: UID ${user.uid} 职业从 ${user.profession} 更新为 ${smartProfession}`);
            user.setProfession(smartProfession);
        }
    }
    getAllUsersData() {
        const r = {};
        for (const [uid, u] of this.users.entries()) {
            // 合并三类技能，保留类型用于前端/历史渲染
            const mergedSkills = [];
            if (u.skillUsage_damage) u.skillUsage_damage.forEach((data, skillId) => {
                mergedSkills.push({
                    skillId,
                    stats: { stats: data.stats.stats, count: data.stats.count },
                    skillInfo: data.skillInfo,
                    meta: data.meta
                });
            });
            if (u.skillUsage_heal) u.skillUsage_heal.forEach((data, skillId) => {
                mergedSkills.push({
                    skillId,
                    stats: { stats: data.stats.stats, count: data.stats.count },
                    skillInfo: data.skillInfo,
                    meta: data.meta
                });
            });
            if (u.skillUsage_takendamage) u.skillUsage_takendamage.forEach((data, skillId) => {
                mergedSkills.push({
                    skillId,
                    stats: { stats: data.stats.stats, count: data.stats.count },
                    skillInfo: data.skillInfo,
                    meta: data.meta
                });
            });

            r[uid] = {
                uid: u.uid,
                name: u.name,
                nickname: u.nickname,
                profession: u.profession,
                total_damage: u.damageStats.stats,
                total_healing: u.healingStats.stats,
                total_dps: u.getTotalDps(),
                total_hps: u.getTotalHps(),
                // 新增：瞬时伤害和治疗
                instant_dps: u.getInstantDps(),
                instant_hps: u.getInstantHps(),
                taken_damage: u.takenDamage,
                total_count: u.getTotalCount(),
                realtime_dps: u.damageStats.realtimeStats.value,
                realtime_dps_max: u.damageStats.realtimeStats.max,
                realtime_hps: u.healingStats.realtimeStats.value,
                realtime_hps_max: u.healingStats.realtimeStats.max,
                // 合并后的技能
                skill_usage: mergedSkills,
                fightPoint: u.fightPoint,
                lastActivityTime: u.lastActivityTime || Date.now()
            };
            // 兼容：透传 nickname/name
            if (!r[uid].nickname && nicknameMap[uid]) r[uid].nickname = nicknameMap[uid];
        }
        return r;
    }
    // 标记
    markPlayerActive(uid) {
        this.activePlayers.set(uid, true);
        // this.checkAndCleanup();
    }

    // 清理
    checkAndCleanup() {
        const now = Date.now();
        // 只有当距离上次清理时间超过间隔，且临时名称数量不少于20时才进行清理
        if ((now - this.lastCleanupTime >= this.CLEANUP_INTERVAL && this.users_info.size >= 20) || this.users_info.size >= 50) {
            logger.info('开始清理临时名称');

            // 只保留活跃玩家
            const activePlayerNames = new Map();
            for (const [uid, isActive] of this.activePlayers.entries()) {
                if (isActive && this.users_info.has(uid)) {
                    activePlayerNames.set(uid, this.users_info.get(uid));
                }
            }

            const removedCount = this.users_info.size - activePlayerNames.size;
            this.users_info = activePlayerNames;
            this.activePlayers.clear(); // 重新开始计算
            this.lastCleanupTime = now;

            logger.info(` 清理完成，移除了${removedCount}个非活跃玩家的临时名称`);
        } else if (this.users_info.size < 20) {
            logger.info(`临时名称数量不足20(当前${this.users_info.size})，不进行清理`);
        }
    }

    clearAll() {
        // 清空战斗数据但保留已知用户表以便保留昵称
        // 仅重置统计字段，不移除对象，这样 nickname/name 仍在
        // for (const [uid, u] of this.users.entries()) {
        //     u.reset();
        //     // 若存在持久昵称，则还原到 name 字段，避免显示为"玩家_id"
        //     if (this.users_info.has(uid)) {
        //         u.setName(this.users_info.get(uid));
        //     }
        // }
        // 不再清空 this.users，以保留昵称映射
        this.users.clear();
        // 注意：不清理users_info，保持名称持久化
    }
}

const userDataManager = new UserDataManager();

// 战斗状态管理
let lastCombatTime = 0;
let combatStartTime = 0; // 新增：记录本次战斗开始时间
let combatCheckInterval = null;
let COMBAT_TIMEOUT = 10000; // 默认10秒，可配置
let combatTimeoutEnabled = true; // 默认启用战斗超时

// 历史记录管理
class CombatHistoryManager {
    constructor() {
        this.histories = new Map(); // 存储历史记录
        this.maxHistories = 50; // 最多保存50条历史记录
        this.recentlyClearedHistoryId = null; // 最近被清空的历史记录ID
    }

    // 保存当前战斗数据到历史记录
    saveCombatHistory(userData, customName = null, isAutoCleared = false) {
        if (!userData || Object.keys(userData).length === 0) return;

        // 移除大量JSON序列化日志以提高性能
        // logger.info('保存战斗历史记录，原始用户数据:', JSON.stringify(userData, null, 2));

        const timestamp = new Date();
        const historyId = timestamp.toISOString().replace(/[:.]/g, '-').slice(0, 19);
        const displayTime = customName || timestamp.toLocaleString('zh-CN');

        // 深拷贝当前数据并计算详细统计
        const historyData = {
            id: historyId,
            timestamp: timestamp.getTime(),
            displayTime: displayTime,
            players: {},
            summary: {
                totalPlayers: 0,
                totalDamage: 0,
                totalHealing: 0,
                totalTaken: 0,
                totalTime: 0
            }
        };

        let maxTime = 0;
        Object.keys(userData).forEach(uid => {
            const player = userData[uid];
            // 忽略1分钟内0伤害且0治疗的玩家，减少冗余
            const playerDurationSec = (player.total_dps > 0) ? ((player.total_damage?.total || 0) / player.total_dps) : 0;
            const isZeroDamage = (player.total_damage?.total || 0) <= 0;
            const isZeroHealing = (player.total_healing?.total || 0) <= 0;
            // 强化过滤：如果完全无战斗（总击数=0且无承伤），也跳过
            const noCounts = !player.total_count || player.total_count.total === 0;
            const noTaken = (player.taken_damage || 0) <= 0;
            if ((playerDurationSec <= 60 && isZeroDamage && isZeroHealing) || (noCounts && isZeroDamage && isZeroHealing && noTaken)) {
                return; // skip this player
            }
            historyData.players[uid] = {
                uid: uid,
                name: player.name || player.nickname || `玩家_${uid}`,
                nickname: player.nickname || '',
                profession: player.profession || '未知',

                // 总伤害数据
                total_damage: { ...player.total_damage },
                total_dps: player.total_dps || 0,

                // 总治疗数据  
                total_healing: player.total_healing ? { ...player.total_healing } : { total: 0 },
                total_hps: player.total_hps || 0,

                // 承伤数据
                taken_damage: player.taken_damage || 0,

                // 统计数据
                total_count: { ...player.total_count },

                // 瞬时数据
                realtime_dps: player.realtime_dps || 0,
                realtime_dps_max: player.realtime_dps_max || 0,
                realtime_hps: player.realtime_hps || 0,
                realtime_hps_max: player.realtime_hps_max || 0,

                // 战斗点数
                fightPoint: player.fightPoint || 0,

                // 计算暴击率和幸运率
                critRate: player.total_count.total > 0 ?
                    ((player.total_count.critical / player.total_count.total) * 100).toFixed(1) : '0.0',
                luckyRate: player.total_count.total > 0 ?
                    ((player.total_count.lucky / player.total_count.total) * 100).toFixed(1) : '0.0',

                // 直接保存技能明细（包含数值与次数），避免后续解析误差
                skillUsage: Array.isArray(player.skill_usage)
                    ? player.skill_usage.reduce((acc, s) => {
                        // 移除频繁的技能处理日志
                        // logger.info(`处理玩家 ${uid} 的技能 ${s.skillId}:`, s);
                        acc[s.skillId] = {
                            stats: { stats: s.stats.stats, count: s.stats.count || {} },
                            skillInfo: s.skillInfo || {},
                            meta: s.meta || {}
                        };
                        return acc;
                    }, {})
                    : (player.skill_usage || {})
            };

            // 累计总计数据
            historyData.summary.totalDamage += player.total_damage?.total || 0;
            historyData.summary.totalHealing += player.total_healing?.total || 0;
            historyData.summary.totalTaken += player.taken_damage || 0;
            historyData.summary.totalPlayers++;

            // 计算最大战斗时间
            if (player.total_dps > 0) {
                const playerTime = (player.total_damage?.total || 0) / player.total_dps;
                if (playerTime > maxTime) maxTime = playerTime;
            }
        });

        historyData.summary.totalTime = Math.round(maxTime);

        // 保存历史记录
        this.histories.set(historyId, historyData);

        // 如果历史记录过多，删除最旧的
        if (this.histories.size > this.maxHistories) {
            const oldestKey = this.histories.keys().next().value;
            this.histories.delete(oldestKey);
        }

        logger.info(`💾 已保存战斗历史记录: ${displayTime} (${historyData.summary.totalPlayers}名玩家)`);

        // 只有自动清空时才记录为最近清空的历史记录（供技能详情查询使用）
        if (isAutoCleared) {
            this.recentlyClearedHistoryId = historyId;
            logger.info(`🔄 设置最近自动清空记录ID: ${historyId}`);
        }

        return historyId;
    }

    // 获取所有历史记录列表
    getHistoryList() {
        const list = [];
        for (const [id, data] of this.histories.entries()) {
            list.push({
                id: id,
                displayTime: data.displayTime,
                totalPlayers: data.summary.totalPlayers,
                totalDamage: data.summary.totalDamage,
                totalHealing: data.summary.totalHealing,
                totalTime: data.summary.totalTime
            });
        }
        // 按时间倒序排列
        return list.sort((a, b) => b.id.localeCompare(a.id));
    }

    // 获取特定历史记录详情
    getHistoryDetails(historyId) {
        return this.histories.get(historyId);
    }

    // 从最近清空的历史记录中获取玩家数据
    getRecentPlayerData(uid) {
        if (!this.recentlyClearedHistoryId) return null;

        const historyData = this.histories.get(this.recentlyClearedHistoryId);
        if (!historyData || !historyData.players) return null;

        return historyData.players[uid];
    }

    // 清空所有历史记录
    clearAll() {
        this.histories.clear();
        this.recentlyClearedHistoryId = null; // 也清空最近清空记录
        logger.info('🗑️ 已清空所有战斗历史记录');
    }
}

const combatHistoryManager = new CombatHistoryManager();

// 启动战斗状态检查
function startCombatTimeoutCheck() {
    if (combatCheckInterval) {
        clearInterval(combatCheckInterval);
        combatCheckInterval = null;
    }

    // 如果战斗超时功能被禁用，直接返回
    if (!combatTimeoutEnabled) {
        logger.info('战斗超时功能已禁用');
        return;
    }

    combatCheckInterval = setInterval(() => {
        const now = Date.now();
        if (lastCombatTime > 0 && (now - lastCombatTime) >= COMBAT_TIMEOUT) {
            // 脱离战斗超过设定时间，先保存历史记录再清空数据
            const currentData = userDataManager.getAllUsersData();
            if (Object.keys(currentData).length > 0) {
                combatHistoryManager.saveCombatHistory(currentData, null, true); // 标记为自动清空
            }
            userDataManager.clearAll();
            logger.info('🧹 战斗超时，已自动清空统计数据');
            lastCombatTime = 0; // 重置战斗时间
            combatStartTime = 0; // 重置战斗开始时间
        }
    }, 1000); // 每秒检查一次
}

// 停止战斗状态检查
function stopCombatTimeoutCheck() {
    if (combatCheckInterval) {
        clearInterval(combatCheckInterval);
        combatCheckInterval = null;
        logger.info('战斗超时检查已停止');
    }
}

// 更新战斗时间
function updateCombatTime() {
    const now = Date.now();
    // 若之前不在战斗中，则标记战斗开始时间
    if (lastCombatTime === 0) {
        combatStartTime = now;
    }
    lastCombatTime = now;
}

// 技能ID到技能名称的映射 (从文件加载)
let skillNameMap = {};
// 用户ID到昵称的映射 (从文件加载)
let nicknameMap = {};

// 职业ID到职业名称的映射
const professionMap = {
    1: '战士',

    // 基于观察到的日志数据添加的职业ID
};

// 加载技能名称配置文件
function loadSkillNames() {
    try {
        const skillConfigPath = path.join(__dirname, 'skill_names.json');
        if (fs.existsSync(skillConfigPath)) {
            const configData = fs.readFileSync(skillConfigPath, 'utf8');
            const config = JSON.parse(configData);
            skillNameMap = config.skill_names || {};
            logger.info(`已加载 ${Object.keys(skillNameMap).length} 个技能名称配置`);
        } else {
            // 如果文件不存在，创建默认配置
            const defaultConfig = {
                skill_names: {
                    1001: '普通攻击',
                    1002: '技能释放',
                    1003: '终极技能',
                    2001: '治疗技能',
                    2002: '辅助技能'
                },
                last_updated: new Date().toISOString()
            };
            fs.writeFileSync(skillConfigPath, JSON.stringify(defaultConfig, null, 4), 'utf8');
            skillNameMap = defaultConfig.skill_names;
            logger.info('已创建默认技能名称配置文件');
        }
    } catch (error) {
        logger.error('加载技能名称配置失败:', error);
        // 使用默认配置
        skillNameMap = {
            1001: '普通攻击',
            1002: '技能释放',
            1003: '终极技能',
            2001: '治疗技能',
            2002: '辅助技能'
        };
    }
}

// // 保存技能名称配置到文件
// function saveSkillNames() {
//     try {
//         const skillConfigPath = path.join(__dirname, 'skill_names.json');
//         const config = {
//             skill_names: skillNameMap,
//             last_updated: new Date().toISOString()
//         };
//         fs.writeFileSync(skillConfigPath, JSON.stringify(config, null, 4), 'utf8');
//         logger.info('技能名称配置已保存到文件');
//     } catch (error) {
//         logger.error('保存技能名称配置失败:', error);
//     }
// }

// // 加载昵称配置文件
// function loadNicknames() {
//     try {
//         const nicknameConfigPath = path.join(__dirname, 'nicknames.json');
//         if (fs.existsSync(nicknameConfigPath)) {
//             const configData = fs.readFileSync(nicknameConfigPath, 'utf8');
//             const config = JSON.parse(configData);
//             nicknameMap = config.nicknames || {};
//             logger.info(`已加载 ${Object.keys(nicknameMap).length} 个昵称配置`);
//         } else {
//             // 如果文件不存在，创建默认配置
//             const defaultConfig = {
//                 nicknames: {},
//                 last_updated: new Date().toISOString()
//             };
//             fs.writeFileSync(nicknameConfigPath, JSON.stringify(defaultConfig, null, 4), 'utf8');
//             nicknameMap = defaultConfig.nicknames;
//             logger.info('已创建默认昵称配置文件');
//         }
//     } catch (error) {
//         logger.error('加载昵称配置失败:', error);
//         nicknameMap = {};
//     }
// }

// 保存昵称配置到文件
// function saveNicknames() {
//     try {
//         const nicknameConfigPath = path.join(__dirname, 'nicknames.json');
//         const config = {
//             nicknames: nicknameMap,
//             last_updated: new Date().toISOString()
//         };
//         fs.writeFileSync(nicknameConfigPath, JSON.stringify(config, null, 4), 'utf8');
//         logger.info('昵称配置已保存到文件');
//     } catch (error) {
//         logger.error('保存昵称配置失败:', error);
//     }
// }

// 技能类型分类
const SKILL_TYPE = {
    DAMAGE: 'damage',
    HEALING: 'healing',
    TAKEN: 'taken',
    MIXED: 'mixed'
};

// 依据累计标记计算聚合技能类型：若既有伤害又有治疗 => mixed；仅治疗 => healing；仅承伤 => taken；默认 damage
// function computeAggregatedSkillType(meta, fallback) {
//     if (!meta) return fallback || SKILL_TYPE.DAMAGE;
//     const { hasDamage, hasHealing, hasTaken } = meta;
//     if (hasTaken && !hasDamage && !hasHealing) return SKILL_TYPE.TAKEN;
//     // 当前版本不再输出复合技能（mixed），分别计入 damage 与 healing
//     // if (hasHealing && hasDamage) return SKILL_TYPE.MIXED;
//     if (hasHealing && !hasDamage) return SKILL_TYPE.HEALING;
//     if (!hasHealing && !hasDamage && hasTaken) return SKILL_TYPE.TAKEN;
//     return fallback || SKILL_TYPE.DAMAGE;
// }

// 根据技能ID和上下文获取技能名称和类型
function getSkillDisplayInfo(skillId, damage, isCrit, luckyValue, hpLessen, isHeal = false, isTaken = false) {
    // 首先检查预定义的技能映射
    let skillName = skillNameMap[skillId] || `技能_${skillId}`;

    // 确定技能类型（收敛判定，避免误把普攻判成复合技能）
    let skillType = SKILL_TYPE.DAMAGE;
    if (isHeal) {
        // 更稳健的治疗判定：来源标记或名称关键字命中
        skillType = SKILL_TYPE.HEALING;
    } else if (isTaken) {
        // 仅在无伤害且有 hpLessen 记录时视为承伤
        skillType = SKILL_TYPE.TAKEN;
    }

    // 如果没有预定义名称，根据类型生成
    if (!skillNameMap[skillId]) {
        if (skillType === SKILL_TYPE.HEALING) {
            skillName = `治疗技能_${skillId}`;
        } else {
            skillName = `伤害技能_${skillId}`;
        }
    }

    return {
        name: skillName,
        type: skillType
    };
}

// 兼容性：保留旧函数名
// function getSkillDisplayName(skillId, damage, isCrit, luckyValue, hpLessen) {
//     return getSkillDisplayInfo(skillId, damage, isCrit, luckyValue, hpLessen).name;
// }

function getConfigFromEnvArgs(devices) {
    // 优先从环境变量读取
    const envDevice = process.env.DEVICE_INDEX;
    const envLog = process.env.LOG_LEVEL;
    const envPort = process.env.PORT || process.env.SERVER_PORT;
    const argv = process.argv.slice(2);
    let argDevice;
    let argLog;
    let argPort;
    for (const a of argv) {
        if (a.startsWith('--device=')) argDevice = a.split('=')[1];
        if (a.startsWith('--log=')) argLog = a.split('=')[1];
        if (a.startsWith('--port=')) argPort = a.split('=')[1];
    }
    const deviceIndex = envDevice ?? argDevice;
    const logLevel = envLog ?? argLog;
    const chosenPort = envPort ?? argPort;
    const nonInteractive =
        deviceIndex !== undefined && deviceIndex !== null &&
        logLevel !== undefined && logLevel !== null &&
        (!devices || devices[deviceIndex]);
    return { deviceIndex, logLevel, port: chosenPort, nonInteractive };
}

async function main() {


    logger.info('Welcome to use Damage Counter for Star Resonance by Dimole!');
    logger.info(`Version: ${VERSION}`);


    await configManager.loadAll();
    skillNameMap = configManager.skillNames.getMap();
    nicknameMap = configManager.nickNames.getMap();


    logger.info("网卡编号列表");
    for (let i = 0; i < devices.length; i++) {
        logger.info(i + '.\t' + devices[i].description);
    }
    logger.emptyLine();

    const { deviceIndex, logLevel, port, nonInteractive } = getConfigFromEnvArgs(devices);
    let num;
    let log_level;
    // 解析 Web 端口
    const parsedPort = (() => {
        const n = Number(port);
        if (Number.isFinite(n) && n >= 1 && n <= 65535) return n;
        return 8989;
    })();

    // 智能网卡检测：先扫描找到合适的网卡，如果找不到则使用用户配置
    if (nonInteractive) {
        num = Number(deviceIndex);
        log_level = (logLevel || 'info');
        rl.close();
    } else {
        // 交互模式下，先尝试自动检测合适的网卡
        const autoDetectedDevice = await autoDetectNetworkDevice();
        if (autoDetectedDevice !== null) {
            logger.info(`✅ 自动检测到合适的网卡: ${autoDetectedDevice.index}. ${autoDetectedDevice.description}`);
            num = autoDetectedDevice.index;

            // 保存为默认配置 - 优先更新 launcher_config.json，如果失败则回退到 server_input.txt
            const fs = require('fs');
            const path = require('path');

            // 尝试更新 launcher_config.json
            let configUpdated = false;
            try {
                // 查找 launcher_config.json 文件
                const possibleConfigPaths = [
                    path.join(__dirname, 'launcher_config.json'),
                    path.join(__dirname, '..', 'launcher_config.json'),
                    path.join(process.cwd(), 'launcher_config.json'),
                    // LocalAppData 位置（打包后常驻）
                    (process.env.LOCALAPPDATA ? path.join(process.env.LOCALAPPDATA, 'StarDPS', 'launcher_config.json') : null)
                ];

                let configPath = null;
                for (const possiblePath of possibleConfigPaths) {
                    if (fs.existsSync(possiblePath)) {
                        configPath = possiblePath;
                        break;
                    }
                }

                if (configPath) {
                    // 读取现有配置
                    const configContent = fs.readFileSync(configPath, 'utf8');
                    const config = JSON.parse(configContent);

                    // 更新 deviceIndex
                    config.deviceIndex = num;
                    if (config.lastDeviceIndex !== undefined) {
                        config.lastDeviceIndex = num;
                    }

                    // 保存更新后的配置
                    fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
                    logger.info(` 已将网卡 ${num} 保存到 launcher_config.json 作为默认配置`);
                    configUpdated = true;
                }
            } catch (error) {
                logger.info(` 更新 launcher_config.json 失败: ${error.message}，将回退到 server_input.txt`);
            }

            // 如果 launcher_config.json 更新失败，回退到 server_input.txt
            if (!configUpdated) {
                const fallbackConfigPath = path.join(__dirname, 'server_input.txt');
                const currentConfig = fs.existsSync(fallbackConfigPath) ? fs.readFileSync(fallbackConfigPath, 'utf8').split('\n') : ['3', 'info'];
                currentConfig[0] = num.toString();
                fs.writeFileSync(fallbackConfigPath, currentConfig.join('\n'));
                logger.info(` 已将网卡 ${num} 保存到 server_input.txt 作为默认配置`);
            }
        } else {
            // 如果自动检测失败，使用用户现有配置
            num = Number(deviceIndex);
            logger.info(` 自动检测失败，使用用户配置的网卡: ${num}`);
        }

        const ansLevel = await ask('Please enter log level (info|debug): ') || 'info';
        if (!ansLevel || !['info', 'debug'].includes(ansLevel)) {
            logger.info('Invalid log level! Use default "info"');
            log_level = 'info';
        } else {
            log_level = ansLevel;
        }
        rl.close();
    }

    // 验证网卡索引的有效性
    if (num < 0 || num >= devices.length) {
        logger.info(` 无效的网卡索引: ${num}，可用范围: 0-${devices.length - 1}，将使用 0 作为默认`);
        num = 0;
    }

    logger.info(` 使用网卡: ${num}. ${devices[num].description}`);

    // const logger = winston.createLogger({
    //     level: log_level,
    //     format: winston.format.combine(
    //         winston.format.colorize({ all: true }),
    //         winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
    //         winston.format.logger.infof(info => {
    //             return `[${info.timestamp}] [${info.level}] ${info.message}`;
    //         })
    //     ),
    //     transports: [
    //         new winston.transports.Console()
    //     ]
    // });
    // // 兜底机制
    // try {
    //     process.removeAllListeners && process.removeAllListeners('uncaughtException');
    //     process.removeAllListeners && process.removeAllListeners('unhandledRejection');
    // } catch { }
    // process.on('uncaughtException', (err) => {
    //     try { logger.error('UncaughtException: ' + (err && err.stack ? err.stack : err)); } catch { }
    // });
    // process.on('unhandledRejection', (reason) => {
    //     try { logger.error('UnhandledRejection: ' + (reason && reason.stack ? reason.stack : reason)); } catch { }
    // });

    // 使用源码的数据包处理器（如果可用），确保治疗/承伤/伤害解析的稳定性
    let packetProcessor = null;
    try {
        if (SRPacketProcessor) {
            packetProcessor = new SRPacketProcessor({ logger, userDataManager });
        }
    } catch (e) {
        logger.info('External packet processor not available, using built-in parser.');
        packetProcessor = null;
    }

    // 瞬时DPS/HPS 更新
    setInterval(() => { userDataManager.updateAllRealtimeDps(); }, 100);

    // 启动战斗超时检查
    startCombatTimeoutCheck();

    //express
    app.use(express.static('public'));
    app.get('/api/data', (req, res) => {
        const user = userDataManager.getAllUsersData();
        // 兼容：透传 nickname/name
        Object.keys(user).forEach(uid => {
            if (!user[uid].nickname && nicknameMap[uid]) user[uid].nickname = nicknameMap[uid];
        });
        res.json({ code: 0, user });
    });
    app.get('/api/clear', (req, res) => {
        // 清空前保存历史记录
        const currentData = userDataManager.getAllUsersData();
        if (Object.keys(currentData).length > 0) {
            // 检查是否是计时桩的自定义命名
            const timerHistory = req.query.timerHistory;
            if (timerHistory) {
                combatHistoryManager.saveCombatHistory(currentData, timerHistory);
            } else {
                combatHistoryManager.saveCombatHistory(currentData);
            }
        }
        userDataManager.clearAll();
        // 重置战斗计时
        lastCombatTime = 0;
        combatStartTime = 0;
        logger.info('Statistics have been cleared!');
        res.json({
            code: 0,
            msg: 'Statistics have been cleared!',
        });
    });

    // 获取历史记录列表
    app.get('/api/history', (req, res) => {
        try {
            const historyList = combatHistoryManager.getHistoryList();
            res.json({
                code: 0,
                data: historyList
            });
        } catch (error) {
            logger.error('获取历史记录失败:', error);
            res.json({
                code: 1,
                msg: '获取历史记录失败: ' + error.message,
                data: []
            });
        }
    });

    // 获取特定历史记录详情
    app.get('/api/history/:historyId', (req, res) => {
        try {
            const historyId = req.params.historyId;
            const historyData = combatHistoryManager.getHistoryDetails(historyId);

            if (!historyData) {
                res.json({
                    code: 1,
                    msg: '历史记录不存在'
                });
                return;
            }

            res.json({
                code: 0,
                data: historyData
            });
        } catch (error) {
            logger.error('获取历史记录详情失败:', error);
            res.json({
                code: 1,
                msg: '获取历史记录详情失败: ' + error.message
            });
        }
    });

    // 清空所有历史记录
    app.delete('/api/history', (req, res) => {
        try {
            combatHistoryManager.clearAll();
            res.json({
                code: 0,
                msg: '所有历史记录已清空'
            });
        } catch (error) {
            logger.error('清空历史记录失败:', error);
            res.json({
                code: 1,
                msg: '清空历史记录失败: ' + error.message
            });
        }
    });

    // 获取战斗超时状态
    app.get('/api/combat-timeout-status', (req, res) => {
        try {
            res.json({
                code: 0,
                timeout: COMBAT_TIMEOUT,
                enabled: combatTimeoutEnabled
            });
        } catch (error) {
            logger.error('获取战斗超时状态失败:', error);
            res.json({
                code: 1,
                msg: '获取失败: ' + error.message
            });
        }
    });

    // 设置战斗超时时间
    app.get('/api/combat-timeout', (req, res) => {
        try {
            const timeout = parseInt(req.query.timeout);
            const enabled = req.query.enabled === 'true';

            // 更新启用状态
            combatTimeoutEnabled = enabled;

            // 如果启用了，更新超时时间
            if (enabled && timeout && timeout >= 1000 && timeout <= 60000) {
                COMBAT_TIMEOUT = timeout;
                logger.info(`战斗超时功能已启用，超时时间: ${timeout}ms`);
            } else if (!enabled) {
                logger.info('战斗超时功能已禁用');
            } else {
                logger.warn(`无效的超时时间: ${timeout}ms，范围应为1-60秒`);
            }

            // 根据启用状态启动或停止战斗超时检查
            if (enabled) {
                startCombatTimeoutCheck();
            } else {
                stopCombatTimeoutCheck();
            }

            res.json({
                code: 0,
                msg: enabled ? '战斗超时功能已启用' : '战斗超时功能已禁用',
                timeout: COMBAT_TIMEOUT,
                enabled: combatTimeoutEnabled
            });
        } catch (error) {
            logger.error('设置战斗超时失败:', error);
            res.json({
                code: 1,
                msg: '设置失败: ' + error.message
            });
        }
    });

    // 获取战斗时长（秒）
    app.get('/api/combat-duration', (req, res) => {
        try {
            if (combatStartTime > 0 && lastCombatTime > 0) {
                const durationSec = Math.floor((Date.now() - combatStartTime) / 1000);
                res.json({ code: 0, duration: durationSec });
            } else {
                res.json({ code: 0, duration: 0 });
            }
        } catch (error) {
            res.json({ code: 1, msg: '获取战斗时长失败' });
        }
    });

    app.post('/api/update-skill-name', express.json(), async (req, res) => {
        const { skillId, skillName } = req.body;
        if (!skillId || !skillName) {
            res.json({
                code: 1,
                msg: 'Missing skillId or skillName'
            });
            return;
        }

        // 更新技能名称映射
        skillNameMap[skillId] = skillName;

        // 保存到文件
        await configManager.saveSingle("skillNames");
        // saveSkillNames();

        logger.info(`Updated skill name: ${skillId} -> ${skillName}`);

        res.json({
            code: 0,
            msg: 'Skill name updated successfully and saved to file'
        });
    });

    app.get('/api/skill-names', (req, res) => {
        res.json({
            code: 0,
            data: skillNameMap
        });
    });

    app.post('/api/update-nickname', express.json(), async (req, res) => {
        const { uid, nickname } = req.body;
        if (!uid) {
            res.json({ code: 1, msg: '用户ID不能为空' });
            return;
        }
        // 空字符串表示删除
        if (nickname === '') {
            delete nicknameMap[uid];
        } else if (typeof nickname === 'string') {
            nicknameMap[uid] = nickname;
        } else {
            res.json({ code: 1, msg: '昵称格式不正确' });
            return;
        }
        await configManager.saveSingle("nickNames")
        // saveNicknames();
        res.json({ code: 0, msg: '昵称更新成功' });
    });

    app.get('/api/nicknames', (req, res) => {
        res.json({
            code: 0,
            data: nicknameMap
        });
    });

    // 健康检查
    app.get('/api/health', (req, res) => {
        try {
            // 检查网卡状态
            const networkStatus = {
                devicesCount: devices ? devices.length : 0,
                currentDevice: num,
                captureActive: c && c.isOpen ? c.isOpen() : false,
                timestamp: Date.now()
            };

            res.json({
                code: 0,
                status: 'ok',
                network: networkStatus,
                uptime: process.uptime()
            });
        } catch (error) {
            logger.error('健康检查失败:', error);
            res.json({
                code: 1,
                status: 'error',
                error: error.message
            });
        }
    });

    // 简化的网卡列表获取 - 只返回基本信息
    app.get('/api/network-devices', (req, res) => {
        try {
            if (!devices || devices.length === 0) {
                return res.json({
                    code: 1,
                    msg: '网卡列表不可用',
                    data: []
                });
            }

            // 简单的网卡列表，不做复杂处理
            const deviceList = devices.map((device, index) => ({
                index: index,
                name: device.name || `网卡_${index}`,
                description: device.description || device.name || `网卡_${index}`,
                isCurrent: index === num
            }));

            res.json({
                code: 0,
                data: deviceList,
                currentDevice: num,
                totalDevices: devices.length
            });
        } catch (error) {
            logger.error('获取网卡列表失败:', error.message);
            res.json({
                code: 1,
                msg: '获取网卡列表失败',
                data: []
            });
        }
    });

    // 简单高效的网卡切换 - 立即生效
    app.post('/api/network-device', express.json(), async (req, res) => {
        try {
            const { deviceIndex } = req.body;
            const newDeviceIndex = Number(deviceIndex);

            if (isNaN(newDeviceIndex) || newDeviceIndex < 0 || newDeviceIndex >= devices.length) {
                return res.json({
                    code: 1,
                    msg: '无效的网卡索引'
                });
            }

            logger.info(`网卡切换: ${devices[newDeviceIndex].description || devices[newDeviceIndex].name}`);

            // 1. 更新配置文件（简单实现）
            const fs = require('fs');
            const path = require('path');

            try {
                const configPaths = [
                    path.join(__dirname, 'launcher_config.json'),
                    path.join(__dirname, '..', 'launcher_config.json'),
                    path.join(process.cwd(), 'launcher_config.json')
                ];

                for (const configPath of configPaths) {
                    if (fs.existsSync(configPath)) {
                        try {
                            const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
                            config.deviceIndex = newDeviceIndex;
                            fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
                            logger.info(`✅ 配置文件已更新`);
                            break;
                        } catch (e) {
                            logger.warn(`配置文件更新失败: ${e.message}`);
                        }
                    }
                }
            } catch (configError) {
                logger.warn('配置文件更新失败，但将继续切换网卡:', configError.message);
            }

            // 2. 立即切换抓包服务（简单重启）
            try {
                logger.info('关闭当前抓包...');

                // 停止数据包处理以避免干扰
                isPaused = true;

                if (c) {
                    try { if (c.close) c.close(); } catch { }
                    c = null;
                }

                // 短暂等待资源释放
                await new Promise(resolve => setTimeout(resolve, 300));

                // 重新初始化新网卡
                logger.info('启动新网卡抓包...');
                const device = devices[newDeviceIndex].name;
                const filter = 'ip and tcp';
                const bufSize = 10 * 1024 * 1024;
                const buffer = Buffer.alloc(bufSize);

                c = new Cap();
                const linkType = c.open(device, filter, bufSize, buffer);

                if (linkType === 'ETHERNET') {
                    num = newDeviceIndex; // 更新全局网卡索引

                    // 重启监听器处理
                    isPaused = false; // 恢复数据包处理
                    attachCapPacketListener(c, buffer, linkType);
                    logger.info(`✅ 网卡切换成功: ${devices[num].description}`);

                    res.json({
                        code: 0,
                        msg: '网卡已成功切换，抓包服务已启动',
                        data: {
                            deviceIndex: newDeviceIndex,
                            deviceName: devices[newDeviceIndex].name,
                            deviceDescription: devices[newDeviceIndex].description
                        }
                    });
                } else {
                    throw new Error(`不支持的网卡类型: ${linkType}`);
                }
            } catch (switchError) {
                logger.error('网卡切换失败:', switchError.message);
                res.json({
                    code: 1,
                    msg: '网卡切换失败: ' + switchError.message + '，请尝试重启程序'
                });
            }
        } catch (error) {
            logger.error('网卡切换处理失败:', error.message);
            res.json({
                code: 1,
                msg: '网卡切换处理失败: ' + error.message
            });
        }
    });

    // 仅重建抓包
    app.post('/api/reload-capture', async (req, res) => {
        try {
            logger.info('开始重载抓包服务...');

            // 暂停数据包处理
            isPaused = true;

            // 安全关闭现有抓包
            if (c) {
                try {
                    if (c.removeAllListeners) c.removeAllListeners('packet');
                } catch (e) {
                    logger.warn('移除抓包监听器失败:', e.message);
                }
                try {
                    if (c.close) c.close();
                } catch (e) {
                    logger.warn('关闭抓包失败:', e.message);
                }
                c = null;
            }

            // 等待资源释放
            await new Promise(resolve => setTimeout(resolve, 500));

            // 重新初始化抓包
            const device = devices[num].name;
            const filter = 'ip and tcp';
            const bufSize = 10 * 1024 * 1024;
            const buffer = Buffer.alloc(bufSize);

            logger.info(`重新初始化抓包: ${devices[num].description} (${device})`);

            c = new Cap();
            const linkType = c.open(device, filter, bufSize, buffer);

            if (linkType === 'ETHERNET') {
                // 重启监听器处理
                isPaused = false;
                attachCapPacketListener(c, buffer, linkType);

                // 等待抓包稳定
                logger.info('等待抓包服务稳定...');
                await new Promise(resolve => setTimeout(resolve, 1000));

                logger.info(`✅ 抓包已成功重载: ${devices[num].description} (${devices[num].name})`);

                res.json({
                    code: 0,
                    msg: '抓包服务已重载',
                    deviceIndex: num,
                    deviceName: devices[num].name,
                    deviceDescription: devices[num].description
                });
            } else {
                throw new Error('抓包重载失败: 非以太网类型');
            }
        } catch (error) {
            logger.error('reload-capture 失败:', error);

            // 尝试恢复抓包
            try {
                if (c) {
                    try { if (c.removeAllListeners) c.removeAllListeners('packet'); } catch { }
                    try { if (c.close) c.close(); } catch { }
                    c = null;
                }

                // 等待资源释放
                await new Promise(resolve => setTimeout(resolve, 1000));

                // 重新尝试初始化
                const device = devices[num].name;
                const filter = 'ip and tcp';
                const bufSize = 10 * 1024 * 1024;
                const buffer = Buffer.alloc(bufSize);

                c = new Cap();
                const linkType = c.open(device, filter, bufSize, buffer);
                if (linkType === 'ETHERNET') {
                    attachCapPacketListener(c, buffer, linkType);

                    // 等待抓包稳定
                    await new Promise(resolve => setTimeout(resolve, 1500));

                    // 验证抓包是否正常工作
                    if (!c || !c.isOpen || !c.isOpen()) {
                        throw new Error('抓包重载后验证失败');
                    }

                    logger.info('抓包服务已恢复');
                    res.json({ code: 0, msg: '抓包服务已恢复', deviceIndex: num });
                } else {
                    throw new Error('恢复抓包失败: 非以太网类型');
                }
            } catch (recoveryError) {
                logger.error('恢复抓包失败:', recoveryError.message);

                // 即使恢复失败，也不返回错误，而是尝试继续运行
                logger.warn('抓包重载失败，但服务将继续运行...');
                res.json({
                    code: 1,
                    msg: '抓包重载失败: ' + error.message + '，但服务将继续运行',
                    deviceIndex: num
                });
            }
        }
    });

    app.get('/api/skill-stats/:uid', (req, res) => {
        const uid = Number(req.params.uid);
        let user = userDataManager.getUser(uid);
        let isFromHistory = false;

        // 如果当前用户不存在，尝试从最近清空的历史记录中获取
        if (!user) {
            const recentPlayerData = combatHistoryManager.getRecentPlayerData(uid);
            if (recentPlayerData) {
                user = recentPlayerData;
                isFromHistory = true;
                logger.info(`从历史记录中获取玩家${uid}的技能数据`);
            } else {
                return res.json({ code: 1, msg: 'User not found', data: [] });
            }
        }

        const totalDamage = isFromHistory ?
            (user.total_damage?.total || 0) :
            (user.damageStats?.stats?.total || 0);
        const totalHealing = isFromHistory ?
            (user.total_healing?.total || 0) :
            (user.healingStats?.stats?.total || 0);
        const totalTaken = isFromHistory ?
            (user.taken_damage || 0) :
            (user.takenDamage || 0);

        const arr = [];
        const processSkillData = (skillData, skillId) => {
            const stat = skillData.stats || skillData; // 兼容旧格式
            const skillInfo = skillData.skillInfo || { name: `技能_${skillId}`, type: 'damage' };
            const total = stat?.stats?.total ?? stat?.total ?? 0;
            const hpLessen = stat?.stats?.hpLessen ?? stat?.hpLessen ?? 0;
            const count = stat?.count?.total ?? stat?.countTotal ?? 0;
            const critCount = stat?.count?.critical ?? stat?.countCritical ?? 0;
            const luckyCount = stat?.count?.lucky ?? stat?.countLucky ?? 0;
            const critRate = count > 0 ? ((critCount / count) * 100).toFixed(1) : '0.0';
            const luckyRate = count > 0 ? ((luckyCount / count) * 100).toFixed(2) : '0.0';

            // 根据技能类型计算显示值和百分比
            let displayValue = total;
            let percentage = 0;
            if (skillInfo.type === 'taken') {
                displayValue = hpLessen; // 承伤技能显示承伤数值
                percentage = totalTaken > 0 ? ((hpLessen / totalTaken) * 100).toFixed(2) : 0;
            } else if (skillInfo.type === 'healing') {
                displayValue = total; // 治疗技能显示治疗数值
                percentage = totalHealing > 0 ? ((total / totalHealing) * 100).toFixed(2) : 0;
            } else {
                displayValue = total; // 伤害技能显示伤害数值
                percentage = totalDamage > 0 ? ((total / totalDamage) * 100).toFixed(2) : 0;
            }

            arr.push({
                skill_id: Number(skillId),
                skill_name: skillInfo.name,
                skill_type: skillInfo.type,
                total_damage: displayValue,
                count,
                crit_count: critCount,
                crit_rate: critRate,
                lucky_count: luckyCount,
                lucky_rate: luckyRate,
                percentage: percentage
            });
        };

        // 合并三个来源：damage / heal / taken
        if (isFromHistory) {
            // 历史数据：尽力处理（历史里 skillUsage_damage 保存在对象 skillUsage 或 total 结构中）
            const damageUsage = user.skillUsage_damage || user.skillUsage || {};
            Object.keys(damageUsage).forEach(skillId => processSkillData(damageUsage[skillId], skillId));
            // 历史版本可能没有独立的 heal/taken 聚合，忽略即可
        } else {
            if (user.skillUsage_damage) user.skillUsage_damage.forEach((sd, sid) => processSkillData(sd, sid));
            if (user.skillUsage_heal) user.skillUsage_heal.forEach((sd, sid) => processSkillData(sd, sid));
            if (user.skillUsage_takendamage) user.skillUsage_takendamage.forEach((sd, sid) => processSkillData(sd, sid));
        }

        // 根据查询参数进行排序（修复技能筛选显示逻辑）
        const sortType = req.query.sort || 'damage'; // 默认按伤害排序

        arr.sort((a, b) => {
            // 优先按技能类型分组排序
            if (sortType === 'healing' || sortType === 'heal') {
                // 治疗筛选：治疗技能优先，然后是伤害，最后承伤
                const typeOrder = { 'healing': 0, 'damage': 1, 'taken': 2 };
                const typeCompare = (typeOrder[a.skill_type] || 2) - (typeOrder[b.skill_type] || 2);
                if (typeCompare !== 0) return typeCompare;
            } else if (sortType === 'taken') {
                // 承伤筛选：承伤技能优先
                const typeOrder = { 'taken': 0, 'damage': 1, 'healing': 2 };
                const typeCompare = (typeOrder[a.skill_type] || 2) - (typeOrder[b.skill_type] || 2);
                if (typeCompare !== 0) return typeCompare;
            }
            // 同类型内按数值降序排序
            return b.total_damage - a.total_damage;
        });

        // 同时返回 data 与 skills 以兼容前端不同版本的字段期望
        res.json({ code: 0, data: arr, skills: arr });
    });

    // 控制监听器的API
    app.post('/api/listener-control', express.json(), (req, res) => {
        try {
            const { action } = req.body;

            if (action === 'stop') {
                isPaused = true;
                res.json({ code: 0, msg: '监听器已停止', active: false });
            } else if (action === 'start') {
                isPaused = false;
                res.json({ code: 0, msg: '监听器已启动', active: true });
            } else if (action === 'status') {
                res.json({ code: 0, msg: '监听器状态', active: !isPaused });
            } else {
                res.json({ code: 1, msg: '无效的操作，支持: stop, start, status' });
            }
        } catch (error) {
            logger.error('控制监听器失败:', error.message);
            res.json({ code: 1, msg: '控制监听器失败: ' + error.message });
        }
    });

    // 明确绑定到 IPv4，避免在部分环境仅监听到 IPv6 (::1) 导致 127.0.0.1 访问被拒绝
    const serverHost = '127.0.0.1';
    const serverInstance = app.listen(parsedPort, serverHost, () => {
        logger.info(`Web Server started at http://${serverHost}:${parsedPort}`);

        // 启动抓包健康检查
        // 移除复杂的健康检查启动
        logger.info('抓包健康检查已启动');
    });
    serverInstance.on('error', (err) => {
        logger.error(`Web Server listen error on ${serverHost}:${parsedPort}: ${err.message}`);
        if (err.code === 'EADDRINUSE') {
            logger.error('端口被占用，请关闭占用 8989 的程序或修改启动端口');
        }
    });

    logger.info('Welcome!');
    logger.info('Attempting to find the game server, please wait!');

    let user_uid;
    let current_server = '';
    let _data = Buffer.alloc(0);
    let tcp_next_seq = -1;
    let tcp_cache = {};
    let tcp_cache_size = 0;
    let tcp_last_time = 0;
    const tcp_lock = new Lock();

    const processPacket = (buf) => {
        try {
            // 优先使用源码解析器，内部已正确区分伤害/治疗/承伤
            if (packetProcessor && typeof packetProcessor.processPacket === 'function') {
                try {
                    packetProcessor.processPacket(buf);
                    return; // 已处理，无需再走旧逻辑
                } catch (ppErr) {
                    logger.debug('PacketProcessor fallback:', ppErr.message);
                }
            }
            if (buf.length < 32) return;
            if (buf[4] & 0x80) {//zstd
                if (!zlib.zstdDecompressSync) logger.warn('zstdDecompressSync is not available! Please check your Node.js version!');
                const decompressed = zlib.zstdDecompressSync(buf.subarray(10));
                buf = Buffer.concat([buf.subarray(0, 10), decompressed]);
            }
            const data = buf.subarray(10);
            if (data.length) {
                const stream = Readable.from(data, { objectMode: false });
                let data1;
                do {
                    const len_buf = stream.read(4);
                    if (!len_buf) break;
                    data1 = stream.read(len_buf.readUInt32BE() - 4);
                    try {
                        let body = pb.decode(data1.subarray(18)) || {};
                        if (data1[17] === 0x2e) {
                            body = body[1];
                            if (body[5]) { //玩家uid
                                const uid = BigInt(body[5]) >> 16n;
                                if (user_uid !== uid) {
                                    user_uid = uid;
                                    logger.info('Got player UID! UID: ' + user_uid);
                                }
                            }
                        }

                        // 深度分析用户信息（昵称、职业等）
                        try {
                            // 记录原始数据包信息
                            const packetType = data1[17];
                            // 减少频繁的包类型日志以提高性能
                            // logger.debug(`Packet type: 0x${packetType.toString(16)}, length: ${data1.length}`);

                            if (body && typeof body === 'object') {
                                // 详细记录数据包结构
                                const bodyKeys = Object.keys(body);
                                // 减少频繁的body分析日志
                                // logger.debug(`Body keys: [${bodyKeys.join(', ')}], depth: ${JSON.stringify(body).length}`);



                                // 全方位深度数据包分析
                                const deepAnalyzePacket = (obj, path = '', depth = 0, maxDepth = 8) => {
                                    if (!obj || depth > maxDepth) return;

                                    // 记录详细路径信息
                                    if (depth <= 2) {
                                        logger.debug(`Analyzing path: ${path || 'root'}, type: ${typeof obj}, keys: ${Object.keys(obj || {}).length}`);
                                    }

                                    if (Array.isArray(obj)) {
                                        obj.forEach((item, index) => {
                                            if (item && typeof item === 'object') {
                                                const currentPath = `${path}[${index}]`;

                                                // 深度分析每个数组元素
                                                let detectedUid = null;
                                                let detectedName = null;
                                                let detectedClass = null;
                                                let allFields = {};

                                                // 收集所有字段信息
                                                for (const [key, value] of Object.entries(item)) {
                                                    allFields[key] = {
                                                        type: typeof value,
                                                        value: value,
                                                        length: typeof value === 'string' ? value.length : null
                                                    };

                                                    // 更广泛的UID检测
                                                    if (typeof value === 'bigint' || (typeof value === 'number' && value > 100000)) {
                                                        const possibleUid = typeof value === 'bigint' ? value >> 16n : BigInt(value) >> 16n;
                                                        if (possibleUid > 0 && possibleUid < 999999999n) {
                                                            detectedUid = possibleUid;
                                                            logger.debug(`Potential UID found: ${possibleUid} at ${currentPath}.${key}`);
                                                        }
                                                    }

                                                    // 更全面的昵称检测
                                                    if (typeof value === 'string' && value.length >= 1 && value.length <= 30) {
                                                        // 检测中文、英文、数字组合
                                                        if (/[\u4e00-\u9fa5a-zA-Z0-9_\-\.]/.test(value) &&
                                                            !/^[\d\.]+$/.test(value) && // 排除纯数字
                                                            !/^[0-9a-f]{8,}$/i.test(value) && // 排除hash值
                                                            value.length > 1) {
                                                            detectedName = value;
                                                            logger.debug(`Potential name found: "${value}" at ${currentPath}.${key}`);
                                                        }
                                                    }

                                                    // 更全面的职业检测
                                                    if (typeof value === 'number' && value >= 0 && value <= 100) {
                                                        // 检测可能的职业字段
                                                        const keyStr = key.toString().toLowerCase();
                                                        if (keyStr.includes('class') ||
                                                            keyStr.includes('job') ||
                                                            keyStr.includes('profession') ||
                                                            keyStr.includes('career') ||
                                                            keyStr.includes('role') ||
                                                            key.toString().length <= 3) { // 短字段名可能是职业ID
                                                            detectedClass = value;
                                                            logger.debug(`Potential class found: ${value} at ${currentPath}.${key}`);
                                                        }
                                                    }
                                                }

                                                // 如果找到了有用信息，尝试关联
                                                if (detectedUid || detectedName || detectedClass) {
                                                    logger.info(`Found user data at ${currentPath}:`);
                                                    logger.info(`   UID: ${detectedUid || 'none'}`);
                                                    logger.info(`   Name: ${detectedName || 'none'}`);
                                                    logger.info(`   Class: ${detectedClass || 'none'}`);
                                                    logger.info(`   All fields: ${JSON.stringify(allFields, null, 2)}`);

                                                    // 如果有UID，保存用户信息
                                                    if (detectedUid && user_info[detectedUid]) {
                                                        if (detectedName) {
                                                            user_info[detectedUid].nickname = detectedName;
                                                            logger.info(` Updated nickname for UID ${detectedUid}: ${detectedName}`);
                                                        }
                                                        if (detectedClass !== null) {
                                                            if (professionMap[detectedClass]) {
                                                                user_info[detectedUid].profession = professionMap[detectedClass];
                                                                logger.info(` Updated profession for UID ${detectedUid}: ${professionMap[detectedClass]} (ID: ${detectedClass})`);
                                                            } else {
                                                                logger.info(`❓ Unknown profession ID ${detectedClass} for UID ${detectedUid}`);
                                                            }
                                                        }
                                                    }

                                                    // 即使没有UID，也记录可能的用户信息
                                                    if (!detectedUid && (detectedName || detectedClass)) {
                                                        logger.info(` Orphaned user data (no UID): name="${detectedName}", class=${detectedClass}`);
                                                    }
                                                }

                                                // 继续递归分析
                                                deepAnalyzePacket(item, currentPath, depth + 1, maxDepth);
                                            }
                                        });
                                    } else if (obj && typeof obj === 'object') {
                                        // 分析对象的所有属性
                                        for (const [key, value] of Object.entries(obj)) {
                                            const currentPath = path ? `${path}.${key}` : key;
                                            if (value && typeof value === 'object') {
                                                deepAnalyzePacket(value, currentPath, depth + 1, maxDepth);
                                            }
                                        }
                                    }
                                };

                                // 开始深度分析
                                deepAnalyzePacket(body);

                                // 额外分析：查找字符串模式
                                const findStringPatterns = (obj, path = '') => {
                                    if (!obj || typeof obj !== 'object') return;

                                    if (Array.isArray(obj)) {
                                        obj.forEach((item, index) => {
                                            findStringPatterns(item, `${path}[${index}]`);
                                        });
                                    } else {
                                        for (const [key, value] of Object.entries(obj)) {
                                            if (typeof value === 'string' && value.length > 0) {
                                                logger.debug(`🔤 String at ${path}.${key}: "${value}" (length: ${value.length})`);
                                            } else if (typeof value === 'object' && value !== null) {
                                                findStringPatterns(value, path ? `${path}.${key}` : key);
                                            }
                                        }
                                    }
                                };

                                if (logger.level === 'debug') {
                                    findStringPatterns(body);
                                }
                            }
                        } catch (e) {
                            logger.error('深度分析用户信息时出错:', e.message);
                            logger.debug('Error stack:', e.stack);
                            logger.debug('Error occurred in packet type: 0x' + (typeof packetType !== 'undefined' ? packetType.toString(16) : 'unknown'));
                            if (body) {
                                try {
                                    logger.debug('Body structure:', JSON.stringify(body, null, 2).substring(0, 500) + '...');
                                } catch (jsonError) {
                                    logger.debug('Body structure could not be serialized:', jsonError.message);
                                }
                            }
                        }
                        let body1 = body[1];
                        if (body1) {
                            if (!Array.isArray(body1)) body1 = [body1];
                            for (const b of body1) {
                                if (b[7] && b[7][2]) {
                                    logger.debug(b.toBase64());

                                    // 获取目标UUID（受击者）- 这是关键修复！
                                    const target_uuid = BigInt(b[5] || 0); // b[5]是目标UUID
                                    const is_target_player = (target_uuid & 0xffffn) === 640n;
                                    const target_uid = target_uuid >> 16n;

                                    const hits = Array.isArray(b[7][2]) ? b[7][2] : [b[7][2]];
                                    for (const hit of hits) {
                                        const skill = hit[12];
                                        if (typeof skill !== 'number') break;
                                        const value = hit[6], luckyValue = hit[8], isMiss = hit[2], isCrit = hit[5], hpLessenValue = hit[9] ?? 0;
                                        const damage = value ?? luckyValue;

                                        // 获取攻击者UUID
                                        const attacker_uuid = BigInt(hit[21] || hit[11]);
                                        const is_attacker_player = (attacker_uuid & 0xffffn) === 640n;
                                        const attacker_uid = attacker_uuid >> 16n;

                                        if (!is_attacker_player && !is_target_player) break; // 排除非玩家相关攻击
                                        if (typeof damage !== 'number') break;

                                        // 检测是否为治疗（更宽容的启发式，兼容不同打包/协议偏移）
                                        const heuristicIsHeal = (() => {
                                            if (hit[3] === 2 || hit[0] === 2) return true;
                                            try {
                                                for (const k of [1, 4, 10, 13, 14]) {
                                                    if (hit[k] === 2) return true;
                                                }
                                                // 扫描前若干字段，避免误判
                                                for (let k = 0; k <= 6; k++) {
                                                    if (hit[k] === 2) return true;
                                                }
                                            } catch { }
                                            return false;
                                        })();
                                        const isHeal = heuristicIsHeal;

                                        if (is_target_player) {
                                            // 玩家目标
                                            if (isHeal) {
                                                // 玩家被治疗
                                                if (is_attacker_player) {
                                                    // 只记录玩家造成的治疗，并记录技能ID
                                                    // userDataManager.addHealing(Number(attacker_uid), skill, 0, damage, !!isCrit, !!luckyValue, false, Number(target_uid));
                                                    userDataManager.inside_addHealing(
                                                        Number(attacker_uid),//attacker_uid
                                                        Number(target_uid),//target_uid
                                                        skill,//skillID
                                                        0,//damageElement
                                                        damage,//damage
                                                        hpLessenValue,//hpless
                                                        !!isCrit,//isCrit
                                                        !!luckyValue,//isLucky
                                                        false,//isCauseLucky
                                                    )
                                                    logger.info(`💚 Healing: Attacker ${attacker_uid} healed Target ${target_uid} for ${damage} (skill ${skill})`);
                                                }
                                            } else {
                                                // 玩家受到伤害 - 这是承伤的关键！
                                                // userDataManager.addTakenDamage(Number(target_uid), damage, skill);
                                                userDataManager.inside_addTakenDamage(
                                                    Number(attacker_uid),//attacker_uid
                                                    Number(target_uid),//target_uid
                                                    skill,//skillID
                                                    0,//damageElement
                                                    damage,//damage
                                                    hpLessenValue,//hpless
                                                    !!isCrit,//isCrit
                                                    !!luckyValue,//isLucky
                                                    false,//isCauseLucky
                                                )
                                                logger.info(`🛡️ TakenDamage: Target ${target_uid} took ${damage} damage from ${is_attacker_player ? `Player ${attacker_uid}` : 'Non-Player'} Skill: ${skill}`);
                                            }
                                        } else {
                                            // 非玩家目标
                                            if (isHeal) {
                                                // 治疗非玩家目标，忽略
                                            } else {
                                                // 玩家对非玩家造成伤害
                                                if (is_attacker_player) {
                                                    // userDataManager.addDamage(Number(attacker_uid), skill, 0, damage, !!isCrit, !!luckyValue, hpLessenValue, false);
                                                    userDataManager.inside_addDamage(
                                                        Number(attacker_uid),//attacker_uid
                                                        Number(target_uid),//target_uid
                                                        skill,//skillID
                                                        0,//damageElement
                                                        damage,//damage
                                                        hpLessenValue,//hpless
                                                        !!isCrit,//isCrit
                                                        !!luckyValue,//isLucky
                                                        false,//isCauseLucky
                                                    )
                                                    logger.info(`⚔️ Damage: Attacker ${attacker_uid} dealt ${damage} damage to Non-Player with Skill: ${skill}`);
                                                }
                                            }
                                        }
                                    }
                                } else {
                                    //logger.debug(data1.toString('hex'));
                                }
                            }
                        } else {
                            //logger.debug(data1.toString('hex'));
                        }
                    } catch (e) {
                        logger.debug(e);
                        logger.debug(data1.subarray(18).toString('hex'));
                    }
                } while (data1 && data1.length)
            }
        } catch (e) {
            logger.debug(e);
        }
    }
    const clearTcpCache = () => {
        _data = Buffer.alloc(0);
        tcp_next_seq = -1;
        tcp_last_time = 0;
        tcp_cache = {};
        tcp_cache_size = 0;
    }

    // 简化的暂停控制（参照源码架构）
    let isPaused = false;

    // 简化的抓包监听器
    function attachCapPacketListener(capInstance, bufferRef, linkTypeRef) {
        try {
            capInstance.setMinBytes && capInstance.setMinBytes(0);

            // 简化的错误处理
            capInstance.on('error', (error) => {
                logger.error('抓包错误:', error.message);
            });

            capInstance.on('packet', async function (nbytes, trunc) {
                // 检查是否暂停统计
                if (isPaused) {
                    return; // 暂停时不处理数据包
                }

                try {
                    // 更新数据包计数和时间
                    lastPacketTime = Date.now();
                    packetCount++;

                    // 仅复制实际包长，避免冗余拷贝
                    const buffer1 = Buffer.from(bufferRef.slice(0, nbytes));
                    if (linkTypeRef === 'ETHERNET') {
                        var ret = decoders.Ethernet(buffer1);
                        if (ret.info.type === PROTOCOL.ETHERNET.IPV4) {
                            ret = decoders.IPV4(buffer1, ret.offset);
                            const srcaddr = ret.info.srcaddr;
                            const dstaddr = ret.info.dstaddr;

                            if (ret.info.protocol === PROTOCOL.IP.TCP) {
                                var datalen = ret.info.totallen - ret.hdrlen;

                                ret = decoders.TCP(buffer1, ret.offset);
                                const srcport = ret.info.srcport;
                                const dstport = ret.info.dstport;
                                const src_server = srcaddr + ':' + srcport + ' -> ' + dstaddr + ':' + dstport;
                                datalen -= ret.hdrlen;
                                let buf = Buffer.from(buffer1.subarray(ret.offset, ret.offset + datalen));

                                // 场景切换检测优化
                                if (tcp_last_time && Date.now() - tcp_last_time > 45000) {
                                    logger.info(' 场景切换检测到，重置连接状态');
                                    current_server = '';
                                    clearTcpCache();
                                    // 重置状态跟踪
                                    lastPacketTime = Date.now();
                                }

                                if (current_server !== src_server) {
                                    try {
                                        //尝试通过小包识别服务器
                                        if (buf[4] == 0) {
                                            const data = buf.subarray(10);
                                            if (data.length) {
                                                const stream = Readable.from(data, { objectMode: false });
                                                let data1;
                                                do {
                                                    const len_buf = stream.read(4);
                                                    if (!len_buf) break;
                                                    data1 = stream.read(len_buf.readUInt32BE() - 4);
                                                    const signature = Buffer.from([0x00, 0x63, 0x33, 0x53, 0x42, 0x00]); //c3SB??
                                                    if (Buffer.compare(data1.subarray(5, 5 + signature.length), signature)) break;
                                                    try {
                                                        let body = pb.decode(data1.subarray(18)) || {};
                                                        if (current_server !== src_server) {
                                                            current_server = src_server;
                                                            clearTcpCache();
                                                            logger.info('Got Scene Server Address: ' + src_server);

                                                            // 动态优化过滤器：收窄到具体服务器IP
                                                            try {
                                                                if (c && typeof c.setFilter === 'function') {
                                                                    const serverIp = srcaddr;
                                                                    const newFilter = `tcp and host ${serverIp}`;
                                                                    c.setFilter(newFilter);
                                                                    logger.info(`🎯 已收窄抓包过滤至服务器: ${serverIp}`);
                                                                }
                                                            } catch (filterError) {
                                                                logger.warn(`设置动态过滤器失败: ${filterError.message}`);
                                                            }

                                                            // 重置健康检查状态，避免场景切换时误判
                                                            // 重置状态跟踪
                                                            lastPacketTime = Date.now();
                                                        }
                                                        if (data1[17] === 0x2e) {
                                                            body = body[1];
                                                            if (body[5]) { //玩家uid
                                                                if (!user_uid) {
                                                                    user_uid = BigInt(body[5]) >> 16n;
                                                                    logger.info('Got player UID! UID: ' + user_uid);
                                                                }
                                                            }
                                                        }
                                                    } catch (e) { }
                                                } while (data1 && data1.length)
                                            }
                                        }
                                    } catch (e) { }
                                    return;
                                }
                                //这里已经是识别到的服务器的包了
                                await tcp_lock.acquire();
                                if (tcp_next_seq === -1 && buf.length > 4 && buf.readUInt32BE() < 999999) { //第一次抓包可能抓到后半段的，先丢了
                                    tcp_next_seq = ret.info.seqno;
                                }
                                logger.debug('TCP next seq: ' + tcp_next_seq);
                                tcp_cache[ret.info.seqno] = buf;
                                tcp_cache_size++;
                                while (tcp_cache[tcp_next_seq]) {
                                    const seq = tcp_next_seq;
                                    _data = _data.length === 0 ? tcp_cache[seq] : Buffer.concat([_data, tcp_cache[seq]]);
                                    tcp_next_seq = (seq + tcp_cache[seq].length) >>> 0; //uint32
                                    tcp_cache[seq] = undefined;
                                    tcp_cache_size--;
                                    tcp_last_time = Date.now();
                                    setTimeout(() => {
                                        if (tcp_cache[seq]) {
                                            tcp_cache[seq] = undefined;
                                            tcp_cache_size--;
                                        }
                                    }, 10000);
                                }
                                while (_data.length > 4) {
                                    let len = _data.readUInt32BE();
                                    if (_data.length >= len) {
                                        const packet = _data.subarray(0, len);
                                        _data = _data.subarray(len);
                                        processPacket(packet);
                                    } else {
                                        if (len > 999999) {
                                            logger.error(`Invalid Length!! ${_data.length},${len},${_data.toString('hex')},${tcp_next_seq}`);
                                            // 不再退出进程，清空缓存并跳出，避免 cmd 退出导致 network error
                                            clearTcpCache();
                                            break;
                                        }
                                        break;
                                    }
                                }
                                tcp_lock.release();
                            } else
                                logger.error('Unsupported IPv4 protocol: ' + PROTOCOL.IP[ret.info.protocol]);
                        } else
                            logger.error('Unsupported Ethertype: ' + PROTOCOL.ETHERNET[ret.info.type]);
                    }
                } catch (packetError) {
                    logger.error('处理数据包时出错:', packetError.message);
                }
            });
        } catch (setupError) {
            logger.error('设置抓包监听器失败:', setupError.message);
        }
    }

    //抓包相关
    let c = null;
    try {
        c = new Cap();
        const device = devices[num].name;
        const filter = 'ip and tcp';
        const bufSize = 10 * 1024 * 1024;
        const buffer = Buffer.alloc(bufSize);

        logger.info(`🔍 正在初始化网卡: ${devices[num].description} (${device})`);

        let linkType;
        try {
            linkType = c.open(device, filter, bufSize, buffer);
        } catch (openError) {
            // 详细的错误诊断
            const errorMsg = openError.message.toLowerCase();
            if (errorMsg.includes('access') || errorMsg.includes('permission')) {
                throw new Error('权限不足：请以管理员权限运行程序');
            } else if (errorMsg.includes('device') || errorMsg.includes('adapter')) {
                throw new Error('网卡设备不可用：可能被其他程序占用或已禁用');
            } else if (errorMsg.includes('driver') || errorMsg.includes('npcap') || errorMsg.includes('winpcap')) {
                throw new Error('驱动问题：请确认已安装 Npcap（WinPcap 兼容模式）');
            } else {
                throw new Error(`网卡初始化失败: ${openError.message}`);
            }
        }

        if (linkType === 'ETHERNET') {
            attachCapPacketListener(c, buffer, linkType);
            logger.info(`✅ 网卡初始化成功: ${devices[num].description}`);
        } else {
            throw new Error(`不支持的链路类型: ${linkType}，仅支持以太网设备`);
        }
    } catch (error) {
        logger.error(`❌ 网卡初始化失败: ${error.message}`);
        logger.error(`网卡: ${devices[num].description} (${devices[num].name})`);
        logger.error('请检查：');
        logger.error('1. 网卡是否被其他程序占用');
        logger.error('2. 是否有足够的权限访问网卡');
        logger.error('3. 网卡驱动是否正常');

        // 尝试使用默认网卡（索引0）
        if (num !== 0 && devices.length > 0) {
            logger.info('🔄 尝试使用默认网卡...');
            num = 0;
            try {
                if (c && c.close) c.close();
                c = new Cap();
                const device = devices[0].name;
                const filter = 'ip and tcp';
                const bufSize = 10 * 1024 * 1024;
                const buffer = Buffer.alloc(bufSize);

                logger.info(`🔍 正在初始化默认网卡: ${devices[0].description} (${device})`);

                let linkType;
                try {
                    linkType = c.open(device, filter, bufSize, buffer);
                } catch (openError) {
                    const errorMsg = openError.message.toLowerCase();
                    if (errorMsg.includes('access') || errorMsg.includes('permission')) {
                        throw new Error('权限不足：请以管理员权限运行程序');
                    } else if (errorMsg.includes('driver') || errorMsg.includes('npcap') || errorMsg.includes('winpcap')) {
                        throw new Error('驱动问题：请确认已安装 Npcap（WinPcap 兼容模式）');
                    } else {
                        throw new Error(`默认网卡初始化失败: ${openError.message}`);
                    }
                }

                if (linkType === 'ETHERNET') {
                    attachCapPacketListener(c, buffer, linkType);
                    logger.info(`✅ 默认网卡初始化成功: ${devices[0].description}`);
                } else {
                    throw new Error(`默认网卡不支持的链路类型: ${linkType}`);
                }
            } catch (fallbackError) {
                logger.error(`❌ 默认网卡也初始化失败: ${fallbackError.message}`);
                logger.error('程序无法继续运行，请检查网卡配置');

                // 不退出程序，而是尝试继续运行并等待用户手动修复
                logger.error('程序将继续运行，但抓包功能可能不可用');
                logger.error('请检查网卡配置后重启程序');

                // 设置一个标志，表示抓包不可用
                global.captureUnavailable = true;
            }
        } else {
            logger.error('程序无法继续运行，请检查网卡配置');

            // 不退出程序，而是尝试继续运行并等待用户手动修复
            logger.error('程序将继续运行，但抓包功能可能不可用');
            logger.error('请检查网卡配置后重启程序');

            // 设置一个标志，表示抓包不可用
            global.captureUnavailable = true;
        }
    }

    // 简化的状态跟踪（参照源码架构）
    let lastPacketTime = Date.now();
    let packetCount = 0;

    // 移除复杂的健康检查机制，参照源码简洁架构

    // 简洁的定时清理机制（参照源码架构）
    setInterval(() => {
        if (tcp_last_time && Date.now() - tcp_last_time > 45000) {
            logger.info('Scene transition or game disconnection detected, resetting cache');
            current_server = '';
            clearTcpCache();
        }
    }, 10000);

    // 获取抓包状态信息
    app.get('/api/capture-status', (req, res) => {
        try {
            const now = Date.now();
            const timeSinceLastPacket = now - lastPacketTime;
            const isHealthy = timeSinceLastPacket <= 30000;

            res.json({
                code: 0,
                data: {
                    isHealthy: isHealthy,
                    packetCount: packetCount,
                    lastPacketTime: lastPacketTime,
                    timeSinceLastPacket: timeSinceLastPacket,
                    timeSinceLastPacketSeconds: Math.round(timeSinceLastPacket / 1000),
                    captureActive: c !== null,
                    currentDevice: num,
                    deviceName: devices[num] ? devices[num].name : 'Unknown',
                    deviceDescription: devices[num] ? devices[num].description : 'Unknown'
                }
            });
        } catch (error) {
            logger.error('获取抓包状态失败:', error);
            res.json({
                code: 1,
                msg: '获取抓包状态失败: ' + error.message
            });
        }
    });
}

main();
